package core

import (
	"context"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"github.com/spf13/viper"
	"net/http"
	"sync"
	"time"
	"uss-root/utils"
)

var upgrader websocket.Upgrader
var wsOnce sync.Once

func NewUpgrader() *websocket.Upgrader {
	wsOnce.Do(
		//创建upgrader
		func() {
			size := viper.GetInt("size")
			utils.Logger.Info().Int("real size:", size).Send()
			if size == 0 {
				upgrader = websocket.Upgrader{
					CheckOrigin: func(r *http.Request) bool { return true }, //CORS
				}
			} else {
				upgrader = websocket.Upgrader{
					ReadBufferSize:  size,
					WriteBufferSize: size,
					CheckOrigin:     func(r *http.Request) bool { return true }, //CORS
				}
			}
		})
	return &upgrader
}

func WsServer(c *gin.Context) {
	customerToken := c.Query("customer_token")
	//进行websocket连接
	conn, err := NewUpgrader().Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		utils.Logger.Error().Msg("websocket upgrade fail")
		c.AbortWithStatus(500)
		return
	}
	defer func() {
		utils.HandleErr(conn.Close())
	}()

	ctx, receiveCancel := context.WithCancel(context.Background())
	//会话结束的现场处理
	defer func() {
		// 取消读取消息协程,防止 goroutine 泄露
		receiveCancel()
		// 返回个 200 ， 不然啥都不返回会报一系列 panic
		c.Status(200)
	}()
	//开启接收消息协程
	messageChan := make(chan string, 16)
	go receiveMsg(ctx, conn, messageChan, customerToken)
	//设定会话超时
	timeoutTick := time.NewTimer(time.Duration(60) * time.Second)
	defer timeoutTick.Stop()
	//循环处理消息
	for {
		select {
		//收到要发送给后端的消息
		case _ = <-messageChan:
			utils.Logger.Debug().Msg("txChan")
			timeoutTick.Reset(time.Duration(60) * time.Second) //重置会话超时定时器
			utils.HandleErr(conn.WriteJSON(gin.H{"code": 200}))
		//收到超时提醒
		case <-timeoutTick.C:
			utils.Logger.Info().Msg("chat timeout")
			timeoutHandle(conn)
			return
		}
	}
}

//负责从websocket读取用户需要发送的内容
func receiveMsg(ctx context.Context, conn *websocket.Conn, msgChan chan string, ct string) {
	for {
		//读取消息
		n, buf, err := conn.ReadMessage()
		select {
		case <-ctx.Done(): //当上下文有取消协程的消息时需要退出协程
			utils.Logger.Info().Msg("receive message goroutine quit")
			return
		default: //在此循环片段没有退出指令
		}
		if err != nil { //对于websocket读取消息错误进行处理
			utils.Logger.Info().Msgf("->read %v's message fail:%v", ct, err)
			return
		}
		msgChan <- string(buf[:n])
	}
}
func timeoutHandle(conn *websocket.Conn) {
	utils.Logger.Info().Msg("session timeout")
	utils.HandleErr(conn.WriteJSON(gin.H{"code": 500}))
}
